#include "Utility.h"

#include "SByte.h"
#include "SChar.h"
#include "SNumber.h"
#include "SString.h"
#include "SEnum.h"
#include "SVector.h"
#include "SStruct.h"

#include "ExpressionResolutor.h"

bool Utility::IsComplexType(SType type)
{	
	if ( type == STypeEnum || type == STypeVector || type == STypeStruct )
	{
		return true;
	}
	
	return false;
}

bool Utility::IsSimpleType(SType type)
{	
	return !IsComplexType(type);
}

long Utility::HexToDec(string s)
{	
	// Table to map hexadecimal digit to decimal value
	struct	HexMap	
	{
		char c;
		long v;
	};		
	static HexMap hexMap[16] =
	{
		{'0', 0}, {'1', 1},	{'2', 2}, {'3', 3},	{'4', 4}, {'5', 5},	{'6', 6}, {'7', 7},	
		{'8', 8}, {'9', 9}, {'A', 10}, {'B', 11}, {'C', 12}, {'D', 13}, {'E', 14}, {'F', 15}
	};

	// Make the conversion
	long n = 0;

	for(unsigned int i=0; i<s.size(); ++i)
	{
	     n <<= 4;

		 for(unsigned int j=0; j<16; j++)
		 {	
			 s[i] = toupper(s[i]);
			 
			 if ( hexMap[j].c == s[i] )
			 {
				 n |= hexMap[j].v;
			 }
		 }
	}

	return n;
}

string Utility::GetTypeName(SType type)
{
	if ( type == STypeByte)   return "SByte";
	if ( type == STypeNumber) return "SNumber";
	if ( type == STypeChar)   return "SChar";
	if ( type == STypeString) return "SString";
	if ( type == STypeEnum)   return "SEnum";
	if ( type == STypeVector) return "SVector";
	if ( type == STypeStruct) return "SStruct";

	return "?";
}

SType Utility::GetTypeFromName(string type)
{
	to_lower(type);

	if ( type == "sbyte")   return STypeByte;
	if ( type == "snumber") return STypeNumber;
	if ( type == "schar")   return STypeChar;
	if ( type == "sstring") return STypeString;
	if ( type == "senum")   return STypeEnum;
	if ( type == "svector") return STypeVector;
	if ( type == "sstruct") return STypeStruct;

	throw SArgumentException("type is not valid.");
}

SValueSp Utility::SimpleParse(SType type, string s)
{
	if ( type == STypeByte)   return SByte::Parse(s);
	if ( type == STypeNumber) return SNumber::Parse(s);
	if ( type == STypeChar)   return SChar::Parse(s);
	if ( type == STypeString) return SString::Parse(s);

	return SValueSp();
}

SValueSp Utility::ComplexParse(TypeDescriptorSp typeDescriptor, string s, SValueSpMapSp vars)
{
	if ( typeDescriptor->Describe(STypeEnum) )
	{
		return SEnum::Parse( typeDescriptor->As<SEnumTypeDescriptor>(), s);
	}
	if ( typeDescriptor->Describe(STypeVector) )
	{
		return SVector::Parse( typeDescriptor->As<SVectorTypeDescriptor>(), s, vars);
	}

	return SStruct::Parse( typeDescriptor->As<SStructTypeDescriptor>(), s, vars);
}

bool Utility::IsValidName( string name )
{
	regex reg("[a-zA-Z_][0-9a-zA-Z_]*");
	return regex_match(name, reg);
}

std::string Utility::GetFullyQualifiedName( string pluginName, string name )
{
	return pluginName + "." + name;
}

void Utility::MergeArgs( string stringArgs, StringVector &args )
{
	StringVector tempArgs;
	split( tempArgs, stringArgs, is_any_of(",") );

	string mergeArg;
	int openBrackets = 0;   
	bool merging = false;   // a merge operation is in course ?

	BOOST_FOREACH(string arg, tempArgs)
	{		
		// Count the number of open and close brackets
		BOOST_FOREACH(char c, arg)
		{
			if ( c == '{' )	
			{
				++openBrackets;
			}
			else if ( c == '}' )
			{
				--openBrackets;
			}

			if ( openBrackets < 0 )
			{
				throw SParseException("Syntax error: brackes not balanced.");
			}
		}

		if ( merging )
		{
			mergeArg += "," + arg;
		}

		if ( openBrackets > 0 )   // arg is not a complete parameter
		{
			if ( !merging )
			{
				mergeArg = arg;
				merging = true;
			}
		}
		else // merging end or normal parameters (openBrackets is 0)
		{
			if ( merging )
			{
				args.push_back( mergeArg );
				merging = false;
				mergeArg = "";
			}
			else
			{
				args.push_back( arg );
			}
		}
	}

	if ( merging )
	{
		args.push_back( mergeArg );
	}
}

string Utility::Format( string format, SValueSpVectorSp listVars, SValueSpMapSp vars)
{	
	ostringstream os;

	bool insideParenthesis = false;
	int p = 0;

	for(int i=0; i<(int)format.size(); ++i)
	{		
		// Check escape sequences
		if ( format[i] == '\\' && i < (int) format.size() - 1)
		{
			char c = format[i+1];

			if ( c == '{' ) { os <<  "{"; ++i; continue; }
			if ( c == '}' ) { os <<  "}"; ++i; continue; }
			if ( c == 'n' ) { os << "\n"; ++i; continue; }
			if ( c == 't' ) { os << "\t"; ++i; continue; }
			if ( c == 'r' ) { os << "\r"; ++i; continue; }
			if ( c == '\\') { os << "\\"; ++i; continue; }
			if ( c == '\"') { os << "\""; ++i; continue; }
		}

		if ( format[i] == '{' )
		{	
			p = i;
			insideParenthesis = true;
		}
		else if ( format[i] == '}' )
		{
			string formatItemString = format.substr( p + 1, i - p - 1);

			trim(formatItemString);

			StringVector formatItems;
			split( formatItems, formatItemString, is_any_of(",") );

			string indexString = "";
			string formatString = "";

			if ( formatItems.size() > 0 )
			{
				indexString = formatItems[0];
				trim(indexString);

				if ( formatItems.size() > 1 )
				{
					formatString = formatItems[1];
					trim(formatString);
				}
			}

			int index = 0;
			try
			{
				index = (int) SNumber::Parse( indexString )->Get();
			}
			catch(...)
			{
				throw SArgumentException("The format item {" + formatItemString + "} is invalid");
			}

			try
			{
				if ( index >= (int) listVars->size() )
				{
					throw SArgumentException("In the variable list there are missing arguments.");					
				}

				// Call the ToString method with the specified formatString
				os << (*listVars)[index]->ToString(formatString);
			}
			catch(...)
			{
				throw SArgumentException("In the variable list there are missing arguments.");
			}

			insideParenthesis = false;
		}
		else if ( !insideParenthesis )
		{
			os << format[i];
		}
	}

	return string( os.str() );
}

string Utility::Format( string format, string listVarsString, SValueSpMapSp vars )
{
	trim(listVarsString);

	// Build the list vars from listVarsString
	SValueSpVectorSp listVars(new SValueSpVector);

	if ( !listVarsString.empty() )
	{
		StringVector expressions;
		split( expressions, listVarsString, is_any_of(" ") );
		
		BOOST_FOREACH( string expression, expressions )
		{		
			SValueSp var = ExpressionResolutor::Resolve(expression, vars);
			listVars->push_back(var);
		}
	}

	return Utility::Format( format, listVars, vars);
}

std::string Utility::Format( string format, string listVarsString, SStructSp str )
{
	SValueSpMapSp vars = str->GetAllMembersAsMap();

	return Utility::Format( format, listVarsString, vars );
}

std::string Utility::Format( string format, SValueSpVectorSp listVars, SStructSp str )
{
	SValueSpMapSp vars = str->GetAllMembersAsMap();

	return Utility::Format( format, listVars, vars );
}

bool Utility::IsPrintable( char c )
{
	return (c >= 32 && c <= 126);
}

SNumberSp Utility::BuildNumber( SByteSp b1, SByteSp b2 )
{
	if ( !b1 || !b2 )
	{
		throw SException("b1 and/or b2 cannot be null.");
	}

	unsigned long b1v = (unsigned long) b1->Get();
	unsigned long b2v = (unsigned long) b2->Get();

	return SNumber::Create( b1v * 256 + b2v);
}

void Utility::SplitNumber( SByteSp b, SByteSp& b1, SByteSp& b2 )
{
	if ( !b )
	{
		throw SException("b cannot be null.");
	}

	unsigned char v = b->Get();
	unsigned char v1 = v / 16;
	unsigned char v2 = v % 16;

	// Set outputs
	b1 = SByte::Create(v1);
	b2 = SByte::Create(v2);
}

string Utility::GetPrettyPrintVar( SValueSp var )
{
	ostringstream os;

	SType type = var->GetType();

	switch(type)
	{
	case STypeByte:
		os << var->ToString("x") << " ( " << var->ToString("d") << " )";
		break;

	case STypeNumber:
		os << var->ToString("x") << " ( " << var->ToString("d") << " )";
		break;

	case STypeEnum:
		os << var->ToString("") << " ( " << var->ToString("x") << " )";
		break;

	case STypeVector:

		if ( var->As<SVector>()->IsBuffer() )
		{
			os << var->ToString("x") << " (" << var->ToString("a") << ")";
		}
		else
		{
			os << var->ToString("pp");
		}

		break;

	case STypeStruct:
		os << endl;
		os << var->ToString("pp");
		break;

	default:
		os << var->ToString();
		break;
	}

	return os.str();
}